package com.xh.core.service.impl;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import com.xh.core.properties.XhProperties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.gson.JsonElement;
import com.xh.core.bean.XhPage;
import com.xh.core.service.IElasticSearchService;
import com.xh.core.util.StringUtils;

import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.core.Bulk;
import io.searchbox.core.BulkResult;
import io.searchbox.core.Delete;
import io.searchbox.core.DeleteByQuery;
import io.searchbox.core.Get;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import io.searchbox.core.SearchResult.Hit;
import io.searchbox.core.Update;
import io.searchbox.indices.ClearCache;
import io.searchbox.indices.CloseIndex;
import io.searchbox.indices.DeleteIndex;
import io.searchbox.indices.Flush;
import io.searchbox.indices.IndicesExists;
import io.searchbox.indices.Optimize;

/**
 * es操作实现类
 */

@Service
public class ElasticSearchImpl implements IElasticSearchService {

	static protected final Log log = LogFactory.getLog(ElasticSearchImpl.class.getName());

	/*
	 * @Autowired private InitElasticSearchConfig esConfig ;
	 */
	@Autowired
	private JestClient client;
	@Autowired
	private XhProperties xhProperties;

	@Override
	public <T> JestResult addIndex(T o, String id, String index, String type) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		Index index1 = new Index.Builder(o).index(index).type(type).id(id).build();
		JestResult jestResult = null;
		try {
			jestResult = client.execute(index1);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return jestResult;
	}
	
	@Override
	public <T> boolean batchInsert(List<T> list, String index, String type) {
		// 批量数据操作结果
		try {
			index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
			type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
			Bulk.Builder bulkBuilder = new Bulk.Builder();
			// 循环构造批量数据
			for (T t : list) {
				Index indexDoc = new Index.Builder(t).index(index).type(type).build();
				bulkBuilder.addAction(indexDoc);
			}
			BulkResult br = client.execute(bulkBuilder.build());
			return br.isSucceeded();
		} catch (Exception e) {
			log.error("ESJestClient.batchInsertDoc-exception", e);
		}
		return false;
	}
	
	@Override
	public <T> boolean insertOrUpdateDoc(T t, String id, String index, String type) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		Index.Builder builder = new Index.Builder(t);
		builder.id(id);
		builder.refresh(true);
		Index indexDoc = builder.index(index).type(type).build();
		try {
			JestResult result = client.execute(indexDoc);
			return result.isSucceeded();
		} catch (IOException e) {
			log.error("ESJestClient insertDoc exception", e);
		}
		return false;
	}
	
	@Override
	public <T> boolean updateIndex(T t, String id, String index, String type) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		Index.Builder builder = new Index.Builder(t);
		builder.id(id);
		builder.refresh(true);
		Index indexDoc = builder.index(index).type(type).build();
		try {
			JestResult result = client.execute(indexDoc);
			return result.isSucceeded();
		} catch (IOException e) {
			log.error("ESJestClient insertDoc exception", e);
		}
		return false;
	}
	
	@Override
	public <T> List<T> queryAll(String index, String type, String param, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		if (StringUtils.isEmpty(param)) {
			SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
			searchSourceBuilder.query(QueryBuilders.matchAllQuery());
			param = searchSourceBuilder.toString();
		}
		Search search = new Search.Builder(param).addType(type).addIndex(index).build();
		List<T> result = null;
		try {
			result = client.execute(search).getSourceAsObjectList(t, false);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	@Override
	public JestResult deleteIndex(String index) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		DeleteIndex deleteIndex = new DeleteIndex.Builder(index).build();
		JestResult result = null;
		try {
			result = client.execute(deleteIndex);
			log.info("deleteIndex == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public JestResult clearCache() {
		ClearCache closeIndex = new ClearCache.Builder().build();
		JestResult result = null;
		try {
			result = client.execute(closeIndex);
			log.info("clearCache == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public JestResult closeIndex(String index) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		CloseIndex closeIndex = new CloseIndex.Builder(index).build();
		JestResult result = null;
		try {
			result = client.execute(closeIndex);
			log.info("closeIndex == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public JestResult optimizeIndex() {
		Optimize optimize = new Optimize.Builder().build();
		JestResult result = null;
		try {
			result = client.execute(optimize);
			log.info("optimizeIndex == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public JestResult flushIndex() {
		Flush flush = new Flush.Builder().build();
		JestResult result = null;
		try {
			result = client.execute(flush);
			log.info("flushIndex == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public JestResult indicesExists() {
		IndicesExists indicesExists = new IndicesExists.Builder("article").build();
		JestResult result = null;
		try {
			result = client.execute(indicesExists);
			log.info("indicesExists == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public JestResult updateDocument(String script, String index, String type, String id) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		/*
		 * String script = "{" + "    \"doc\" : {" +
		 * "        \"title\" : \""+article.getTitle()+"\"," +
		 * "        \"content\" : \""+article.getContent()+"\"," +
		 * "        \"author\" : \""+article.getAuthor()+"\"," +
		 * "        \"source\" : \""+article.getSource()+"\"," +
		 * "        \"url\" : \""+article.getUrl()+"\"," +
		 * "        \"pubdate\" : \""+new
		 * SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(article.getPubdate()
		 * )+"\"" + "    }" + "}";
		 */
		Update update = new Update.Builder(script).index(index).type(type).id(id).build();
		JestResult result = null;
		try {
			result = client.execute(update);
			log.info("updateDocument == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public JestResult deleteDocument(String id, String index, String type) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		Delete delete = new Delete.Builder(id).index(index).type(type).build();
		JestResult result = null;
		try {
			result = client.execute(delete);
			log.info("deleteDocument == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public JestResult deleteDocumentByQuery(String index, String type, String params) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		DeleteByQuery db = new DeleteByQuery.Builder(params).addIndex(index).addType(type).build();

		JestResult result = null;
		try {
			result = client.execute(db);
			log.info("deleteDocument == " + result.getJsonString());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public <T> T getDocument(String index, String type, String id, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		Get get = new Get.Builder(index, id).type(type).build();
		JestResult result = null;
		try {
			result = client.execute(get);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result.getSourceAsObject(t);

	}

	@Override
	public <T> List<Hit<T, Void>> createSearch(String keyWord, String type, T o, String... fields) {
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(QueryBuilders.queryStringQuery(keyWord));
		HighlightBuilder highlightBuilder = new HighlightBuilder();
		for (String field : fields) {
			highlightBuilder.field(field);// 高亮field
		}
		highlightBuilder.preTags("<em>").postTags("</em>");// 高亮标签
		highlightBuilder.fragmentSize(200);// 高亮内容长度
		searchSourceBuilder.highlighter(highlightBuilder);
		Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex(type).build();
		SearchResult result = null;
		List<?> hits = null;
		try {
			result = client.execute(search);
			System.out.println("本次查询共查到：" + result.getTotal() + "个结果！");

			hits = result.getHits(o.getClass());

		} catch (IOException e) {
			e.printStackTrace();
		}
		return (List<Hit<T, Void>>) hits;
	}

	@Override
	public <T> void bulkIndex(String index, String type, T o) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		Bulk bulk = new Bulk.Builder().defaultIndex(index).defaultType(type)
				.addAction(Arrays.asList(new Index.Builder(o).build())).build();
		try {
			client.execute(bulk);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	
	
	@Override
	public <T> List<T> queryAll(Map<String, Object> map, String index, String type, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		return queryAll(mapToQueryBuilder(map), index, type, t);
	}

	@Override
	public <T> List<T> queryAll(QueryBuilder query, String index, String type, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(query);
		Search search = new Search.Builder(searchSourceBuilder.toString()).addType(type).addIndex(index).build();
		List<T> result = null;
		try {
			result = client.execute(search).getSourceAsObjectList(t, false);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public <T> List<T> termQuery(String field, Object text, String index, String type, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(QueryBuilders.termQuery(field, text));
		Search search = new Search.Builder(searchSourceBuilder.toString()).addType(type).addIndex(index).build();
		List<T> result = null;
		try {
			result = client.execute(search).getSourceAsObjectList(t, false);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public <T> List<T> termsQuery(String field, Collection<?> texts, String index, String type, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(QueryBuilders.termsQuery(field, texts));
		Search search = new Search.Builder(searchSourceBuilder.toString()).addType(type).addIndex(index).build();
		List<T> result = null;
		try {
			result = client.execute(search).getSourceAsObjectList(t, false);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	@Override
	public <T> List<T> matchQuery(String field, Object text, String index, String type, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(QueryBuilders.matchQuery(field, text));
		Search search = new Search.Builder(searchSourceBuilder.toString()).addType(type).addIndex(index).build();
		List<T> result = null;
		try {
			result = client.execute(search).getSourceAsObjectList(t, false);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;  
	}
	
	@Override
	public Map matchQueryPercent(String field, Object text, String percent, String index, String type) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
		searchSourceBuilder.query(QueryBuilders.matchQuery(field, text).minimumShouldMatch(percent));
		Search search = new Search.Builder(searchSourceBuilder.toString()).addType(type).addIndex(index).build();
		SearchResult result = null;
		try {
			result = client.execute(search);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result.getJsonMap();
	}

	@Override
	public Map matchQueryPercent(String subjectField, String subjectId, String field, String text, String percent, String index, String type) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);

		BoolQueryBuilder boolQueryBuilder = QueryBuilders
				.boolQuery().must(QueryBuilders.termQuery("subjectId", subjectId))
				.must(QueryBuilders.matchQuery("title", text).minimumShouldMatch(percent));

		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

		searchSourceBuilder.query(boolQueryBuilder);

		Search search = new Search.Builder(searchSourceBuilder.toString()).addType(type).addIndex(index).build();

		SearchResult result = null;
		try {
			result = client.execute(search);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result.getJsonMap();
	}
	@Override
	public <T> List<T> fuzzyQuery(String field, Object text, String index, String type, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(QueryBuilders.fuzzyQuery(field, text).fuzziness(Fuzziness.AUTO));
		Search search = new Search.Builder(searchSourceBuilder.toString()).addType(type).addIndex(index).build();
		List<T> result = null;
		try {
			result = client.execute(search).getSourceAsObjectList(t, false);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	@Override
	public <T> XhPage<T> queryByPage(QueryBuilder query, String index, String type, XhPage<T> page, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(query);
		searchSourceBuilder.from((int)((page.getCurrent()-1)*page.getSize()));
		searchSourceBuilder.size((int)page.getSize());
		if (page.getSortName() != null) {
			searchSourceBuilder.sort(page.getSortName());
		}
		Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex(index).addType(type).build();
		SearchResult result = null;
		try {
			result = client.execute(search);
		} catch (IOException e) {
			e.printStackTrace();
		}
		JsonElement hits = result.getJsonObject().get("hits");
		int hitCount = hits == null ? 0 : hits.getAsJsonObject().get("total").getAsInt();
		page.setTotal(hitCount);
		page.setRecords(result.getSourceAsObjectList(t, false));
		return page;
	}

	private <T> Long getTotal(QueryBuilder query, String index, String type, Class<T> t){
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(query);
		Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex(index).addType(type).build();
		SearchResult result = null;
		try {
			result = client.execute(search);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result.getTotal();
	}
	
	@Override
	public <T> XhPage<T> queryByPage(Map<String, Object> map, String index, String type, XhPage<T> page, Class<T> t) {
		index = String.format("%s_%s", xhProperties.getCachePrefixKey(), index);
		type = String.format("%s_%s", xhProperties.getCachePrefixKey(), type);
		return queryByPage(mapToQueryBuilder(map),index, type, page, t);
	}
	
	/**
	 * 将map转换成query
	 * @param map
	 * @return
	 */
	private QueryBuilder mapToQueryBuilder(Map<String, Object> map){
		BoolQueryBuilder query = QueryBuilders.boolQuery();
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			Object value = entry.getValue();
			if (value instanceof Collection) {
				query.must(QueryBuilders.termsQuery(entry.getKey(), value));
			} else {
				query.must(QueryBuilders.termQuery(entry.getKey(), value));
			}
		}
		return query;
	}

}
